package org.opens.lambda.stream;

import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import org.junit.Test;
import org.opens.guava.utilites.StopWatchTest;
import org.opens.lambda.Banana;
import org.opens.lambda.UserEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class SimpleStream {

    Logger logger = LoggerFactory.getLogger(SimpleStream.class);

    @Test
    public void test1() {
        List<UserEntity> list = UserEntity.getList(5);
        System.out.println(list);
    }

    /**
     * 功能:
     *      分别使用传统方式和lambda的方式过滤list并排序.
     * 输出:
     *      [User{id=748650993, name='Amj0f', age=71}, User{id=-1324094972, name='28Sts', age=52}, User{id=1940469426, name='sJWEy', age=25}, User{id=2111803208, name='mXgd0', age=55}, User{id=1485439868, name='wOewb', age=43}]
     *      [User{id=1940469426, name='sJWEy', age=25}, User{id=1485439868, name='wOewb', age=43}, User{id=-1324094972, name='28Sts', age=52}, User{id=2111803208, name='mXgd0', age=55}, User{id=748650993, name='Amj0f', age=71}]
     *      [User{id=1940469426, name='sJWEy', age=25}, User{id=1485439868, name='wOewb', age=43}, User{id=-1324094972, name='28Sts', age=52}, User{id=2111803208, name='mXgd0', age=55}, User{id=748650993, name='Amj0f', age=71}]
     *      [sJWEy, wOewb, 28Sts, mXgd0, Amj0f]
     * 说明:
     *      1. 使用传统方式和stream方式处理完是一样的;
     *      2. stream使用了方法推导, 看起来更省事, 清晰明了.
     *      3. 可以再stream的操作中嵌入map操作只提取出需要的属性.
     */
    @Test
    public void test2() {
        List<UserEntity> list = UserEntity.getList(5);
        System.out.println(list);
        System.out.println(mySort(list));
        List<UserEntity> result = list.stream().filter(x -> x.getAge() >= 18).sorted(Comparator.comparingInt(UserEntity::getAge).thenComparing(UserEntity::getId)).collect(Collectors.toList());
        //这种在对处理完的list加一层map, 相当于是只取出结果集中的某个元素, 在这个例子中就是只取name.
        List<String> result1 = list.stream().filter(x -> x.getAge() >= 18).sorted(Comparator.comparingInt(UserEntity::getAge).thenComparing(UserEntity::getId)).map(UserEntity::getName).collect(Collectors.toList());

        System.out.println(result);
        System.out.println(result1);
    }

    /**
     * 功能:
     *      测试stream的单线程处理方式.
     * 输出:
     *      18:05:06.155 [main] INFO org.opens.lambda.stream.SimpleStream - 传统stream处理500000个数据开始.
     *      18:05:06.747 [main] INFO org.opens.lambda.stream.SimpleStream - 传统stream处理500000个数据结束, 用时: 603.6 ms
     * 说明:
     *      1. 可以发现, 使用.stream()方式创建的流处理500000个数据需要半秒多;
     */
    @Test
    public void test3() {
        final int number = 500000;
        Stopwatch started = Stopwatch.createStarted();
        logger.info("传统stream处理{}个数据开始.", number);
        List<UserEntity> list = UserEntity.getList(number);
        List<UserEntity> result = list.stream().filter(x -> x.getAge() >= 18).sorted(Comparator.comparingInt(UserEntity::getAge).thenComparing(UserEntity::getId)).collect(Collectors.toList());
        logger.info("传统stream处理{}个数据结束, 用时: {}", number, started.stop());
    }

    /**
     * 功能:
     *      测试stream的多线程处理方式.
     * 输出:
     *      18:07:17.653 [main] INFO org.opens.lambda.stream.SimpleStream - 传统stream处理500000个数据开始.
     *      18:07:17.754 [main] INFO org.opens.lambda.stream.SimpleStream - 传统stream处理500000个数据结束, 用时: 102.8 ms
     * 说明:
     *      1. 对比发现, 使用parallelStream()方式创建的流只需要1/6的时间就可以处理完50w个数据, 并且这个时间是可以根据cpu的核心数来进一步提高的.
     * 引申:
     *      1. 可以参考test5的方法跟踪堆栈, 查看是否真正是多线程处理.
     */
    @Test
    public void test4() {
        final int number = 500000;
        Stopwatch started = Stopwatch.createStarted();
        logger.info("传统stream处理{}个数据开始.", number);
        List<UserEntity> list = UserEntity.getList(5000);
        List<UserEntity> result = list.parallelStream().filter(x -> x.getAge() >= 18).sorted(Comparator.comparingInt(UserEntity::getAge).thenComparing(UserEntity::getId)).collect(Collectors.toList());
        logger.info("传统stream处理{}个数据结束, 用时: {}", number, started.stop());
    }

    /**
     * 功能:
     *      使用jconsole指令来测试parallelStream()是否真的是多线程处理.
     * 流程:
     *      1. 在当前程序中加入sleep, 使线程暂停;
     *      2. 启动程序, 从命令行程序中记录下当前线程的名称;
     *          - 从日志窗口获取命令行参数
     *          - 上面是从日志窗口中获取的命令行参数, 这行输出的最后的com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 org.opens.lambda.stream.SimpleStream,test5就是当前线程的名称.
     *      3. 在cmd中使用jconsole指令连接到当前进程中, 会看到以下三个线程:
     *          - ForkJoinPool.commonPool-worker-1
     *          - ForkJoinPool.commonPool-worker-2
     *          - ForkJoinPool.commonPool-worker-3
     *          - 这三个线程会在处理完数据之后将结果进行join操作, 也就是等待其他线程处理完毕再合并;
     *      4. 这样就相当于是分工完成任务, 所以: 只要cpu核心多, 使用多线程方式处理数据的速度还是很快的;
     *          - 但是会发现, 处理大量数据时, 堆被使用会快速上升, 所以这也需要一个度, 不能再java程序中处理太大量的数据, 否则会导致内存占用飙升.
     *      5. 使用相同的方式去观察单线程stream, 查看线程的数量.
     * 说明:
     *      1. 官方对于stream的操作分为两类:
     *          - Intermediate operations   中间操作
     *              1. 中间操作并不会导致流关闭;
     *              2. 中间操作会产生一个新的流, 可以继续对这个流操作.
     *          - terminal operations       最终操作
     *              1. 执行了最终操作的流就被关闭了, 然后如果继续尝试操作这个流, 就会报错java.lang.IllegalStateException: stream has already been operated upon or closed
     *      2. Intermediate operations的函数:
     *          - sorted();
     *          - filter();
     *          - map();
     *          - limit();
     *      3. terminal operation的函数:
     *          - forEach();
     *          - collect();
     */
    @Test
    public void test5() {
        final int number = 500000;
        Stopwatch started = Stopwatch.createStarted();
        logger.info("传统stream处理{}个数据开始.", number);
        List<UserEntity> list = UserEntity.getList(5000);
        List<UserEntity> result = list.parallelStream().filter(x -> {
            try {
                //使用sleep使当前线程睡眠, 然后使用jconsole指令来查看堆栈和线程信息.
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return x.getAge() >= 18;
        }).sorted(Comparator.comparingInt(UserEntity::getAge).thenComparing(UserEntity::getId)).collect(Collectors.toList());
        logger.info("传统stream处理{}个数据结束, 用时: {}", number, started.stop());
    }

    /**
     * 功能:
     *      测试stream的单线程处理数据方式.
     * 流程:
     *      1. 流程和多线程的相同, 只是创建的stream的方式是stream方法.
     * 说明:
     *      1. 会发现, 线程中并没有多余的线程, 根据新城的当前状态, 会发现main程序sleep在了测试程序的Thread.sleep()处, 所处线程为main, 所以:
     *          - stream处理的方式是单线程的.
     *      2. 但是观察会发现: 单线程对cpu和内存的使用冲击都不打, 堆栈使用相对平缓, 没有多线程那么猛烈, 多线程相当于使一个人充分发挥他的作用.
     */
    @Test
    public void test6() {
        final int number = 500000;
        Stopwatch started = Stopwatch.createStarted();
        logger.info("传统stream处理{}个数据开始.", number);
        List<UserEntity> list = UserEntity.getList(number);
        List<UserEntity> result = list.stream().filter(x -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return x.getAge() >= 18;
        }).sorted(Comparator.comparingInt(UserEntity::getAge).thenComparing(UserEntity::getId)).collect(Collectors.toList());
        logger.info("传统stream处理{}个数据结束, 用时: {}", number, started.stop());
    }

    /**
     * 功能:
     *      已经操作过一次的流不能重复操作.
     * 输出:
     *      报错: java.lang.IllegalStateException: stream has already been operated upon or closed; 中文: 流已被操作或关闭.
     *
     */
    @Test
    public void test7() {
        List<UserEntity> list = UserEntity.getList(10);
        Stream<UserEntity> stream = list.stream();
        stream.forEach(System.out::println);
        //报错: java.lang.IllegalStateException: stream has already been operated upon or closed
        stream.forEach(System.out::println);
    }

    /**
     * 功能:
     *      stream的执行方式在有多个中间操作时, 并非是上一个处理完下一个才开始处理.
     * 输出:
     *      filter: User{id=1656392534, name='RWDFS', age=26}
     *      map: RWDFS
     *      filter: User{id=1893946453, name='xYxNq', age=37}
     *      map: xYxNq
     *      filter: User{id=1227843631, name='nr93S', age=73}
     *      map: nr93S
     *      filter: User{id=1914711975, name='zmOvZ', age=76}
     *      map: zmOvZ
     *      filter: User{id=-958580991, name='dNEIj', age=40}
     *      map: dNEIj
     *      filter: User{id=-36759665, name='UVNx8', age=38}
     *      map: UVNx8
     *      filter: User{id=2107957212, name='ZwctT', age=39}
     *      map: ZwctT
     *      filter: User{id=-1839635819, name='wtgrl', age=30}
     *      map: wtgrl
     *      filter: User{id=1032959130, name='LzJpV', age=31}
     *      map: LzJpV
     *      filter: User{id=-1110991645, name='aAdNp', age=46}
     *      map: aAdNp
     *      [RWDFS, xYxNq, nr93S, zmOvZ, dNEIj, UVNx8, ZwctT, wtgrl, LzJpV, aAdNp]
     * 说明:
     *      1. 可以发现, 处理的过程是执行一次filter, 执行一次map; 并不是执行完filter再执行map.
     */
    @Test
    public void test8() {
        List<UserEntity> list = UserEntity.getList(10);
        List<String> result = list.stream().filter(x -> {
            System.out.println("filter: " + x);
            return x.getAge() >= 18;
        }).map(y -> {
            System.out.println("map: " + y.getName());
            return y.getName();
        }).collect(Collectors.toList());
        System.out.println(result);
    }

    /**
     * 功能:
     *      使用stream的分组.
     * 输出:
     *      20: [User{id=-1271865339, name='NSye6', age=20}, User{id=73243757, name='kjNeY', age=20}, User{id=1044487331, name='KXtcx', age=20}]
     *      21: [User{id=-406269574, name='QlKim', age=21}, User{id=-1982895434, name='R6s9R', age=21}]
     *      22: [User{id=-779794814, name='BFCET', age=22}, User{id=-2118014825, name='xlr6H', age=22}]
     *      24: [User{id=-1064351106, name='TEL5F', age=24}]
     *      25: [User{id=-1152694626, name='Kx4OE', age=25}, User{id=-2108896390, name='XLYTR', age=25}]
     *      26: [User{id=570195716, name='VgaXn', age=26}, User{id=174141193, name='0bAgp', age=26}, User{id=35968469, name='TZxFk', age=26}]
     *      27: [User{id=-803905106, name='64WaL', age=27}, User{id=598450596, name='y6jqY', age=27}, User{id=451413082, name='kD0m4', age=27}, User{id=-917807973, name='3nhgs', age=27}]
     *      28: [User{id=720596770, name='i7jc0', age=28}, User{id=-407496647, name='JamZ4', age=28}]
     *      30: [User{id=-916597718, name='6RPql', age=30}, User{id=467471439, name='STEZj', age=30}]
     *      31: [User{id=511267068, name='iViiO', age=31}, User{id=142860602, name='Z4Wd4', age=31}]
     *      32: [User{id=1926391432, name='bfzCH', age=32}, User{id=1518065412, name='9WisL', age=32}]
     *      33: [User{id=-932873167, name='15ko7', age=33}, User{id=6913267, name='9byZq', age=33}]
     *      34: [User{id=43709914, name='XsKkD', age=34}, User{id=-325087941, name='GfYu8', age=34}]
     *      35: [User{id=964342139, name='eCayY', age=35}]
     *      36: [User{id=1884211582, name='AFZUH', age=36}]
     *      37: [User{id=-2090170756, name='evtNA', age=37}, User{id=1890672544, name='Q3wKH', age=37}, User{id=-2044412938, name='GqKkA', age=37}]
     *      39: [User{id=1206475083, name='nSvAq', age=39}, User{id=-845749470, name='ludgh', age=39}]
     *      40: [User{id=1412807187, name='Ulgeo', age=40}, User{id=1235784281, name='Iw5EH', age=40}]
     *      41: [User{id=324418608, name='N2I2A', age=41}]
     *      42: [User{id=-1105792366, name='G7XVx', age=42}, User{id=832168360, name='PDPju', age=42}]
     *      44: [User{id=-1201274103, name='NNkpk', age=44}]
     *      45: [User{id=2042938356, name='5XB8i', age=45}, User{id=-1009899313, name='6MGNP', age=45}]
     *      46: [User{id=1507891454, name='dU1go', age=46}, User{id=1178347689, name='ZzgEq', age=46}, User{id=-1122881927, name='1d3f2', age=46}]
     *      47: [User{id=1595013699, name='5EaXq', age=47}, User{id=658448746, name='9gl7p', age=47}]
     *      48: [User{id=-921480746, name='jIjhx', age=48}]
     *      50: [User{id=1101553589, name='IAUrL', age=50}, User{id=-1569010610, name='2NSWP', age=50}]
     *      51: [User{id=846992314, name='WzpVk', age=51}, User{id=517477001, name='OsXBq', age=51}]
     *      52: [User{id=1328781937, name='QyLYm', age=52}, User{id=-334085108, name='uPxQN', age=52}, User{id=378959989, name='bbs88', age=52}]
     *      53: [User{id=2027842648, name='y4rxR', age=53}]
     *      54: [User{id=-1563615099, name='76DCO', age=54}, User{id=1547228610, name='0Jal3', age=54}, User{id=746174937, name='jKWws', age=54}]
     *      55: [User{id=-776992436, name='6SWzt', age=55}]
     *      57: [User{id=702411277, name='SGPsk', age=57}]
     *      58: [User{id=-706452760, name='4hdq6', age=58}, User{id=-1582023235, name='mAGmr', age=58}]
     *      59: [User{id=-1220723641, name='g2Eyh', age=59}, User{id=1198486165, name='AvJ63', age=59}]
     *      60: [User{id=1624222527, name='zdEO6', age=60}]
     *      61: [User{id=-1102614303, name='RRPjX', age=61}, User{id=2081267582, name='iVOhk', age=61}, User{id=-1771226159, name='0AMNZ', age=61}]
     *      62: [User{id=-181714256, name='wQlht', age=62}]
     *      63: [User{id=-1679325772, name='NiRwq', age=63}, User{id=1485566475, name='6mNmd', age=63}]
     *      66: [User{id=1905835615, name='79IgC', age=66}]
     *      67: [User{id=-983618790, name='bWkpw', age=67}, User{id=-966393864, name='XM5D3', age=67}]
     *      68: [User{id=673465614, name='iM2t3', age=68}, User{id=820471119, name='FBwgH', age=68}]
     *      69: [User{id=2018839217, name='hBrW5', age=69}]
     *      70: [User{id=-102239370, name='2ZGJW', age=70}]
     *      71: [User{id=-1205876200, name='44E3M', age=71}, User{id=1328942052, name='tlnFA', age=71}, User{id=-553815707, name='ma6Tl', age=71}, User{id=-1151965517, name='DnodZ', age=71}, User{id=1512171009, name='5OZCs', age=71}]
     *      73: [User{id=-1765094623, name='LkI15', age=73}, User{id=-1126075603, name='t7SpW', age=73}, User{id=-94512079, name='HNaro', age=73}]
     *      74: [User{id=748757768, name='ykjei', age=74}, User{id=621836178, name='6ZEgy', age=74}]
     *      76: [User{id=1265663234, name='8csA6', age=76}, User{id=-824706305, name='7AnmP', age=76}, User{id=1967883483, name='uZdQW', age=76}]
     *      77: [User{id=1644134120, name='NW0IS', age=77}, User{id=1269908319, name='92Yvl', age=77}, User{id=-753040312, name='v7DVK', age=77}]
     *      78: [User{id=-1341790651, name='cJHyj', age=78}, User{id=-792509058, name='zPg6v', age=78}]
     *      79: [User{id=2138613026, name='CGQXA', age=79}]
     */
    @Test
    public void test9() {
        List<UserEntity> list = UserEntity.getList(100);
        Map<Integer, List<UserEntity>> result = list.stream().collect(Collectors.groupingBy(UserEntity::getAge));
        result.forEach((k, v) -> System.out.println(k + ": " + v));
    }


    /**
     * 功能:
     *      实现对list中的人进行筛选出成年人, 然后按照先按年龄排序再按照id排序输出.
     * @param list      list参数
     * @return          筛选过并完成排序的list
     */
    private static List<UserEntity> mySort(List<UserEntity> list) {
        List<UserEntity> result = Lists.newArrayList();
        list.forEach(x -> {
            if(x.getAge() >= 18) {
                result.add(x);
            }
        });
        Collections.sort(result, Comparator.comparingInt(UserEntity::getAge).thenComparing(UserEntity::getId));
        return result;
    }

}
